<template>
  <div class="tutorial-page">
    <div class="tutorial-header">
      <h1>Vue 组件</h1>
      <p>深入理解组件化开发，组件通信和高级特性</p>
    </div>

    <div class="tutorial-content">
      <div class="section">
        <h2>组件基础</h2>
        <p>组件是可复用的 Vue 实例，且带有一个名字。我们把组件定义为可复用的 Vue 实例，因为它们都接收相同的选项，例如 data、computed、watch、methods 以及生命周期钩子等。</p>
        
        <h3>创建组件</h3>
        <pre><code class="language-javascript" v-pre>// 定义一个名为 button-counter 的新组件
const ButtonCounter = {
  data() {
    return {
      count: 0
    }
  },
  template: `
    &lt;button @click="count++"&gt;
      You clicked me {{ count }} times.
    &lt;/button&gt;
  `
}

// 全局注册组件
app.component('button-counter', ButtonCounter)</code></pre>
        
        <h3>使用组件</h3>
        <pre><code class="language-html" v-pre>&lt;div id="app"&gt;
  &lt;button-counter&gt;&lt;/button-counter&gt;
&lt;/div&gt;</code></pre>
      </div>

      <div class="section">
        <h2>组件注册</h2>
        
        <h3>全局注册</h3>
        <pre><code class="language-javascript" v-pre>import { createApp } from 'vue'
import MyComponent from './MyComponent.vue'

const app = createApp({})

// 全局注册组件
app.component('MyComponent', MyComponent)</code></pre>
        
        <h3>局部注册</h3>
        <pre><code class="language-javascript" v-pre>import ComponentA from './ComponentA.vue'
import ComponentB from './ComponentB.vue'

export default {
  components: {
    ComponentA,
    ComponentB
  },
  // ...
}</code></pre>
      </div>

      <div class="section">
        <h2>Props</h2>
        <p>Props 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候，它就变成了那个组件实例的一个 property。</p>
        
        <h3>定义 Props</h3>
        <pre><code class="language-javascript" v-pre>export default {
  props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
}</code></pre>
        
        <h3>Prop 类型</h3>
        <pre><code class="language-javascript" v-pre>export default {
  props: {
    title: String,
    likes: Number,
    isPublished: Boolean,
    commentIds: Array,
    author: Object,
    callback: Function,
    contactsPromise: Promise // 或任何其他构造函数
  }
}</code></pre>
        
        <h3>传递静态或动态 Prop</h3>
        <pre><code class="language-html" v-pre>&lt;!-- 静态赋值 --&gt;
&lt;blog-post title="My journey with Vue"&gt;&lt;/blog-post&gt;

&lt;!-- 动态赋值 --&gt;
&lt;blog-post :title="post.title"&gt;&lt;/blog-post&gt;
&lt;blog-post :likes="42"&gt;&lt;/blog-post&gt;
&lt;blog-post :is-published="false"&gt;&lt;/blog-post&gt;
&lt;blog-post :comment-ids="[234, 266, 273]"&gt;&lt;/blog-post&gt;
&lt;blog-post
  :author="{
    name: 'Veronica',
    company: 'Veridian Dynamics'
  }"
&gt;&lt;/blog-post&gt;</code></pre>
      </div>

      <div class="section">
        <h2>组件事件</h2>
        <p>子组件可以通过 $emit 触发事件，父组件通过 v-on 监听这些事件。</p>
        
        <h3>监听子组件事件</h3>
        <pre><code class="language-html" v-pre>&lt;blog-post
  ...
  @enlarge-text="postFontSize += 0.1"
&gt;&lt;/blog-post&gt;</code></pre>
        
        <h3>触发子组件事件</h3>
        <pre><code class="language-html" v-pre>&lt;button @click="$emit('enlarge-text')"&gt;
  Enlarge text
&lt;/button&gt;</code></pre>
        
        <h3>在组件上使用 v-model</h3>
        <pre><code class="language-javascript" v-pre>export default {
  props: ['modelValue'],
  emits: ['update:modelValue'],
  template: `
    &lt;input
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    &gt;
  `
}</code></pre>
        <pre><code class="language-html" v-pre>&lt;custom-input v-model="searchText"&gt;&lt;/custom-input&gt;</code></pre>
      </div>

      <div class="section">
        <h2>插槽 (Slots)</h2>
        <p>Vue 实现了一套内容分发的 API，这套 API 的设计灵感源自 Web Components 规范草案，将 &lt;slot&gt; 元素作为承载分发内容的出口。</p>
        
        <h3>默认插槽</h3>
        <pre><code class="language-html" v-pre>&lt;!-- AlertBox.vue --&gt;
&lt;div class="alert-box"&gt;
  &lt;strong&gt;Error!&lt;/strong&gt;
  &lt;slot&gt;&lt;/slot&gt;
&lt;/div&gt;</code></pre>
        <pre><code class="language-html" v-pre>&lt;alert-box&gt;
  Something bad happened.
&lt;/alert-box&gt;</code></pre>
        
        <h3>具名插槽</h3>
        <pre><code class="language-html" v-pre>&lt;!-- BaseLayout.vue --&gt;
&lt;div class="container"&gt;
  &lt;header&gt;
    &lt;slot name="header"&gt;&lt;/slot&gt;
  &lt;/header&gt;
  &lt;main&gt;
    &lt;slot&gt;&lt;/slot&gt;
  &lt;/main&gt;
  &lt;footer&gt;
    &lt;slot name="footer"&gt;&lt;/slot&gt;
  &lt;/footer&gt;
&lt;/div&gt;</code></pre>
        <pre><code class="language-html" v-pre>&lt;base-layout&gt;
  &lt;template v-slot:header&gt;
    &lt;h1&gt;Here might be a page title&lt;/h1&gt;
  &lt;/template&gt;

  &lt;p&gt;A paragraph for the main content.&lt;/p&gt;
  &lt;p&gt;And another one.&lt;/p&gt;

  &lt;template v-slot:footer&gt;
    &lt;p&gt;Here's some contact info&lt;/p&gt;
  &lt;/template&gt;
&lt;/base-layout&gt;</code></pre>
        
        <h3>作用域插槽</h3>
        <pre><code class="language-html" v-pre>&lt;!-- MyComponent.vue --&gt;
&lt;div&gt;
  &lt;slot :text="greetingMessage" :count="1"&gt;&lt;/slot&gt;
&lt;/div&gt;</code></pre>
        <pre><code class="language-html" v-pre>&lt;my-component&gt;
  &lt;template v-slot:default="slotProps"&gt;
    {{ slotProps.text }} {{ slotProps.count }}
  &lt;/template&gt;
&lt;/my-component&gt;</code></pre>
      </div>

      <div class="section">
        <h2>动态组件 & 异步组件</h2>
        
        <h3>动态组件</h3>
        <pre><code class="language-html" v-pre>&lt;component :is="currentComponent"&gt;&lt;/component&gt;</code></pre>
        <pre><code class="language-javascript" v-pre>export default {
  data() {
    return {
      currentComponent: 'ComponentA'
    }
  },
  components: {
    ComponentA,
    ComponentB
  }
}</code></pre>
        
        <h3>异步组件</h3>
        <pre><code class="language-javascript" v-pre>import { defineAsyncComponent } from 'vue'

// 简单形式
const AsyncComponent = defineAsyncComponent(() =>
  import('./components/AsyncComponent.vue')
)

// 高级形式
const AsyncComponent = defineAsyncComponent({
  // 加载函数
  loader: () => import('./Foo.vue'),

  // 加载异步组件时使用的组件
  loadingComponent: LoadingComponent,
  // 展示加载组件前的延迟时间，默认为 200ms
  delay: 200,

  // 加载失败后展示的组件
  errorComponent: ErrorComponent,
  // 如果提供了一个 timeout 时间限制，并超时了
  // 也会显示这里配置的报错组件，默认值是：Infinity
  timeout: 3000
})</code></pre>
      </div>

      <div class="section">
        <h2>组件之间的循环引用</h2>
        <p>当使用 webpack (或 Browserify 等) 构建项目时，会遇到 "Failed to mount component: template or render function not defined." 的错误。</p>
        
        <h3>解决方案</h3>
        <pre><code class="language-javascript" v-pre>export default {
  name: 'tree-folder',
  // 在 beforeMount 或 mounted 钩子中注册组件
  beforeMount() {
    this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default
  }
}</code></pre>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

// 定义示例数据以避免渲染错误
const post = ref({
  title: 'My journey with Vue'
})

const likes = ref(42)
const isPublished = ref(false)
const commentIds = ref([234, 266, 273])
const author = ref({
  name: 'Veronica',
  company: 'Veridian Dynamics'
})

const postFontSize = ref(1)
const searchText = ref('')
const currentComponent = ref('ComponentA')

const ComponentA = ref({})
const ComponentB = ref({})

const greetingMessage = ref('Hello Vue!')

// 模拟组件数据
const components = reactive({
  ComponentA: ComponentA,
  ComponentB: ComponentB
})
</script>

<style scoped>
@import '@/assets/tutorial-styles.css';
</style>